﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using wrsm.battlenet.entity;
using wrsm.battlenet.exceptions;
using wrsm.common;
using wrsm.common.entity;
using wrsm.common.utility;

namespace wrsm.battlenet {
    
    /// <summary>
    /// transforms the status of a realm, into a file.
    /// similar to logging, this will be used for future historical data purposes
    /// </summary>
    [CLSCompliant(true)]
    public class RealmStatusFileTransformer : IEntityTransformer, IRealmStatusHistoryTransformer {

        /// <summary>
        /// Root path for all realm status history logging
        /// </summary>
        private readonly string _dataPath;

        /// <summary>
        /// Used for synchronising calls to write to the history file.
        /// </summary>
        private object _writeLock = new object();

        /// <summary>
        /// Initialize Realm Status File Transformer
        /// </summary>
        public RealmStatusFileTransformer() {
            try {
                _dataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
                                     ConfigurationManager.AppSettings["wrsm.battlenet.io.path.localdata"]);
                //Make sure the path already exists
                FSUtility.CreateDirectoryPath(_dataPath);
            } catch (Exception caught) {
                throw new BattleNetApplicationException("Unable to configure local history storage", caught);
            }
        }

        /// <summary>
        /// Write the new realm status to the history file
        /// </summary>
        /// <param name="status">Realm status to transform into the file</param>
        public void TransformRealmHistory(RealmStatus status) {
            lock (_writeLock) {
                string historyFilePath = Path.Combine(_dataPath,
                    ConfigurationManager.AppSettings["wrsm.battlenet.io.realm.history.fileNameTemplate"]);
                using (StreamWriter sr = new StreamWriter(historyFilePath, true)) {
                    sr.Write(status.Name.Trim().PadLeft(25));
                    sr.Write(status.Status ? 1 : 0);
                    sr.Write(Convert.ToString(DateTime.Now.GetUnixEpoch()));
                    sr.WriteLine();
                    sr.Flush();
                }
            }
        }


        /// <summary>
        /// Save Entities, or information related to entities for later use
        /// </summary>
        /// <param name="entities">Entities to save</param>
        public void TransformOut(IEnumerable<Entity> entities) {
            //First, let's put this in a usable form
            List<RealmStatus> realmStatusWrite = new List<RealmStatus>();
            foreach (var entity in entities) {
                if (entity.GetType() == typeof(RealmStatus))
                    realmStatusWrite.Add((RealmStatus)entity);
            }
            lock (_writeLock) {
                string currentFilePath = Path.Combine(_dataPath,
                    ConfigurationManager.AppSettings["wrsm.battlenet.io.realm.current.filenameTemplate"]);

                //Delete this file if it already exists
                if (File.Exists(currentFilePath))
                    File.Delete(currentFilePath);

                //Create the XML output file
                using (XmlWriter xw = XmlWriter.Create(currentFilePath)) {
                    xw.WriteStartDocument();
                    xw.WriteStartElement("realmStatus");
                    foreach (RealmStatus status in realmStatusWrite) {
                        xw.WriteStartElement("realmStatusItem");

                        xw.WriteElementString("realmName", status.Name.Trim());
                        xw.WriteElementString("status", Convert.ToString(status.Status));
                        xw.WriteElementString("type", status.Type.Trim());
                        xw.WriteElementString("population", status.Population.Trim());
                        xw.WriteElementString("locale", status.Locale.Trim());

                        xw.WriteEndElement(); //Realm Status Item
                    }

                    xw.WriteEndElement(); //Realm Status
                    xw.WriteEndDocument();
                    xw.Flush();
                }
            }
            return;
        }

        /// <summary>
        /// Load Entities from saved state
        /// </summary>
        /// <returns>Entities restored</returns>
        public IEnumerable<Entity> TransformIn() {
            List<RealmStatus> restoredEntities = new List<RealmStatus>();

            lock (_writeLock) {
                string currentFilePath = Path.Combine(_dataPath,
                    ConfigurationManager.AppSettings["wrsm.battlenet.io.realm.current.filenameTemplate"]);

                //If File does not exist, we cant do much, just return the empty list
                if (!File.Exists(currentFilePath))
                    return restoredEntities;

                //Open our Realm Status File
                using (XmlReader reader = XmlReader.Create(currentFilePath)) {
                    while (reader.Read()) {
                        if (reader.NodeType == XmlNodeType.Element && reader.Name == "realmStatusItem") {
                            restoredEntities.Add(ReadRealmStatus(reader));
                        }
                    }
                }

            }

            return restoredEntities;
        }

        /// <summary>
        /// Reads a Realm Status from XML file
        /// </summary>
        /// <param name="reader"></param>
        /// <returns></returns>
        private RealmStatus ReadRealmStatus(XmlReader reader) {
            RealmStatus status = new RealmStatus();
            while (reader.Read()) {
                if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "realmStatusItem") {
                    return status;
                } else if (reader.NodeType == XmlNodeType.Element && reader.Name == "realmName") {
                    reader.Read();
                    if (reader.NodeType == XmlNodeType.Text) { status.Name = reader.Value.Trim(); }
                } else if (reader.NodeType == XmlNodeType.Element && reader.Name == "status") {
                    reader.Read();
                    if (reader.NodeType == XmlNodeType.Text) { status.Status = Convert.ToBoolean(reader.Value); }
                } else if (reader.NodeType == XmlNodeType.Element && reader.Name == "type") {
                    reader.Read();
                    if (reader.NodeType == XmlNodeType.Text) { status.Type = reader.Value.Trim(); }
                } else if (reader.NodeType == XmlNodeType.Element && reader.Name == "population") {
                    reader.Read();
                    if (reader.NodeType == XmlNodeType.Text) { status.Population = reader.Value.Trim(); }
                } else if (reader.NodeType == XmlNodeType.Element && reader.Name == "locale") {
                    reader.Read();
                    if (reader.NodeType == XmlNodeType.Text) { status.Locale = reader.Value.Trim(); }
                }
            }
            return status;
        }

    }
}
